package com.funtl.framework.orm.hibernate;

import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.transform.Transformers;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Component;

import com.funtl.framework.common.CommonBean;
import com.funtl.framework.common.CommonReflex;
import com.funtl.framework.common.CommonType;
import com.funtl.framework.exception.DaoException;
import com.funtl.framework.mvc.ssh.abstracts.AbstractEntity;
import com.funtl.framework.mvc.ssh.abstracts.AbstractHibernate;

@Component("hibernateDao")
public class HibernateDaoImpl extends AbstractHibernate implements HibernateDao {
	@Override
	public <T extends AbstractEntity> void clearCatch(Class<T> clazz) throws DaoException {
		try {
			SessionFactory factory = super.getHibernateTemplate().getSessionFactory();
			factory.evict(clazz);
		} catch (HibernateException e) {
			throw new DaoException("clearCatch", e);
		}
	}

	@Override
	public <T extends AbstractEntity> void delete(T entity, String fieldName) throws DaoException {
		try {
			Object obj = get(entity, fieldName);
			if (obj == null) {
				return;
			}
			super.getHibernateTemplate().delete(obj);
		} catch (DataAccessException e) {
			throw new DaoException("delete", e);
		}
	}
	
	@Override
	public boolean excuteSQL(String sql, String[] params) throws DaoException {
		Session session = super.getHibernateTemplate().getSessionFactory().openSession();
		Transaction transaction = session.beginTransaction();
		try {
			SQLQuery sqlQuery = session.createSQLQuery(sql);
			if (params != null){
				for(int i=0;i<params.length;i++){
					sqlQuery.setString(i, params[i]);
				}
			}
			sqlQuery.executeUpdate();
			transaction.commit();
			return true;
		} catch (HibernateException e) {
			transaction.rollback();
			throw new DaoException("excuteSQL", e);
		} finally {
			session.flush();
			session.close();
		}
	}

	@Override
	public <T extends AbstractEntity, V> V get(final T entity, final HibernateParam param) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback<V>() {
				@SuppressWarnings("unchecked")
				@Override
				public V doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql);
					return (V) query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("get", e);
		}
	}
	
	@Override
	public <T extends AbstractEntity, V> V getByCatch(final T entity, final HibernateParam param) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback<V>() {
				@SuppressWarnings("unchecked")
				@Override
				public V doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql);
					return (V) query.setCacheable(true).uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("get", e);
		}
	}
	
	@Override
	public<T extends AbstractEntity> T getByEntity(final T entity) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback<T>() {
				@SuppressWarnings("unchecked")
				@Override
				public T doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					
					String[] order = null;
					Query query = CommonReflexParam(entity, session, hql, order);
					return (T) query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getByEntity", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> long getCount(final T entity) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("SELECT COUNT(*) FROM ");
					hql.append(entity.getClass().getName());
					
					Query query = session.createQuery(hql.toString());
					return query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getCount", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> long getCount(final T entity, final HibernateParam param) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("SELECT COUNT(*) FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql);
					return query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getCount", e);
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> long getCountByEhCatch(final T entity, final HibernateParam param) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("SELECT COUNT(*) FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql);
					return query.setCacheable(true).uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getCount", e);
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public<T extends AbstractEntity> long getCountByEntity(final T entity) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("SELECT COUNT(*) FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					
					String[] order = null;
					Query query = CommonReflexParam(entity, session, hql, order);
					return query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getCountByParam", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity, V> List<V> getList(final T entity, final HibernateParam param, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql, order);
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getList", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> List<T> getList(final T entity, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					order(hql, order);
					
					Query query = session.createQuery(hql.toString());
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getList", e);
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public<T extends AbstractEntity> List<T> getListByEntity(final T entity, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					
					Query query = CommonReflexParam(entity, session, hql, order);
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getListByEntity", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity, V> List<V> getListByEhcatch(final T entity, final HibernateParam param, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql, order);
					return query.setCacheable(true).list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getListByEhcatch", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> List<T> getListByEhcatch(final T entity, final String... order) throws DaoException {
		try {
			List<T> list = super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					order(hql, order);
					
					Query query = session.createQuery(hql.toString());
					return query.setCacheable(true).list();
				}
			});
			return list;
		} catch (DataAccessException e) {
			throw new DaoException("getListByEhcatch", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<Map<String, Object>> getListBySQL(final String sql) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					Query query = session.createSQLQuery(sql).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getListBySQL", e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Map<String, Object>> getListBySQL(String sql, String[] params) throws DaoException {
		Session session = super.getHibernateTemplate().getSessionFactory().openSession();
		try {
			SQLQuery sqlQuery = session.createSQLQuery(sql);
			if (params != null){
				for(int i=0;i<params.length;i++){
					sqlQuery.setString(i, params[i]);
				}
			}
			Query query = sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
			return query.list();
		} catch (HibernateException e) {
			throw new DaoException("getListBySQL", e);
		} finally {
			session.flush();
			session.close();
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> List<T> getPagingList(final T entity, final HibernateParam param, final int pageIndex, final int pageSize, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql, order);
					query.setFirstResult((pageIndex - 1) * pageSize);
					query.setMaxResults(pageSize);
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getPagingList", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> List<T> getPagingList(final T entity, final int pageIndex, final int pageSize, final String... order) throws DaoException {
		try {
			List<T> list = super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					order(hql, order);
					
					Query query = session.createQuery(hql.toString());
					query.setFirstResult((pageIndex - 1) * pageSize);
					query.setMaxResults(pageSize);
					return query.list();
				}
			});
			return list;
		} catch (DataAccessException e) {
			throw new DaoException("getPagingList", e);
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public<T extends AbstractEntity> List<T> getPagingListByEntity(final T entity, final int pageIndex, final int pageSize, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					
					Query query = CommonReflexParam(entity, session, hql, order);
					query.setFirstResult((pageIndex - 1) * pageSize);
					query.setMaxResults(pageSize);
					return query.list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getListByEntity", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity, V> List<V> getPagingListByEhcatch(final T entity, final HibernateParam param, final int pageIndex, final int pageSize, final String... order) throws DaoException {
		try {
			return super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE");
					
					Query query = hibernateParam(param, session, hql, order);
					query.setFirstResult((pageIndex - 1) * pageSize);
					query.setMaxResults(pageSize);
					return query.setCacheable(true).list();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("getPagingListByEhcatch", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T extends AbstractEntity> List<T> getPagingListByEhcatch(final T entity, final int pageIndex, final int pageSize, final String... order) throws DaoException {
		try {
			List<T> list = super.getHibernateTemplate().executeFind(new HibernateCallback() {
				@Override
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					order(hql, order);
					
					Query query = session.createQuery(hql.toString());
					query.setFirstResult((pageIndex - 1) * pageSize);
					query.setMaxResults(pageSize);
					return query.setCacheable(true).list();
				}
			});
			return list;
		} catch (DataAccessException e) {
			throw new DaoException("getPagingListByEhcatch", e);
		}
	}

	@Override
	public <T extends AbstractEntity> void insert(T entity) throws DaoException {
		try {
			super.getHibernateTemplate().save(entity);
		} catch (DataAccessException e) {
			throw new DaoException("insert", e);
		}
	}

	@Override
	public <T extends AbstractEntity> void insertAll(List<T> list) throws DaoException {
		Session session = super.getHibernateTemplate().getSessionFactory().openSession();
		Transaction tx = session.beginTransaction();
		tx.begin();
		try {
			if (list != null && list.size() > 0) {
				for (int i = 0 ; i < list.size() ; i++) {
					session.save(list.get(i));
					if ((i % 50 == 0 || i == (list.size() - 1)) && i != 0) {
						session.flush();
						session.clear();
					}
				}
				tx.commit();
			}
		} catch (HibernateException e) {
			tx.rollback();
			throw new DaoException("insertAll", e);
		} finally {
			session.close();
		}
	}

	@Override
	public <T extends AbstractEntity> void insertOrUpdate(T entity) throws DaoException {
		try {
			super.getHibernateTemplate().saveOrUpdate(entity);
		} catch (DataAccessException e) {
			throw new DaoException("insertOrUpdate", e);
		}
	}
	
	@Override
	public <T extends AbstractEntity> void insertOrUpdate(List<T> list) throws DaoException {
		Session session = super.getHibernateTemplate().getSessionFactory().openSession();
		Transaction tx = session.beginTransaction();
		tx.begin();
		try {
			if (list != null && list.size() > 0) {
				for (int i = 0; i < list.size(); i++) {
					session.saveOrUpdate(list.get(i));
					if ((i % 50 == 0 || i == (list.size() - 1)) && i != 0) {
						session.flush();
						session.clear();
					}
				}
				tx.commit();
			}
		} catch (HibernateException e) {
			tx.rollback();
			throw new DaoException("insertOrUpdate", e);
		} finally {
			session.close();
		}
	}

	@Override
	public <T extends AbstractEntity> void update(T entity, String fieldName) throws DaoException {
		try {
			T t = get(entity, fieldName);
			CommonBean.beanToBean(entity, t);
			super.getHibernateTemplate().merge(entity);
		} catch (DataAccessException e) {
			throw new DaoException("update", e);
		}
	}
	
	/* ---------- 工具类方法 ---------- */
	
	private <T extends AbstractEntity> T get(final T entity, final String idFieldName) throws DaoException {
		try {
			return super.getHibernateTemplate().execute(new HibernateCallback<T>() {
				@SuppressWarnings("unchecked")
				public T doInHibernate(Session session) throws HibernateException, SQLException {
					StringBuffer hql = new StringBuffer();
					hql.append("FROM ");
					hql.append(entity.getClass().getName());
					hql.append(" t");
					hql.append(" WHERE t." + idFieldName + " = ?");
					
					Query query = session.createQuery(hql.toString());
					query.setString(0, String.valueOf(CommonReflex.getValue(entity, idFieldName)));
					return (T) query.uniqueResult();
				}
			});
		} catch (DataAccessException e) {
			throw new DaoException("get", e);
		}
	}
	
	/**
	 * 排序条件
	 * @param hql
	 * @param order
	 */
	private void order(StringBuffer hql, final String... order) {
		if (order == null || order.length == 0 || order.length % 2 != 0) {
			return;
		}
		hql.append(" ORDER BY");
		for (int i = 0; i < order.length; i++) {
			if (i % 2 == 0) {
				hql.append(" t.");
				hql.append(order[i]);
			} else {
				hql.append(" ");
				hql.append(order[i]);
				if (i < order.length - 1) {
					hql.append(",");
				} else {
					hql.append(" ");
				}
			}
		}
	}
	
	/**
	 * Hibernate条件
	 * @param param
	 * @param session
	 * @param hql
	 * @return
	 */
	private Query hibernateParam(final HibernateParam param, Session session, StringBuffer hql, String... order) {
		int index = 0;
		for (String propertyType : param.getPropertyTypes()) {
			if (param.getPropertyValues()[index] != null) {
				hql.append(" t.");
				hql.append(propertyType);
				if (index < param.getPropertyTypes().length) {
					hql.append(" ");
					hql.append(param.getOperators()[index]);
					hql.append(" ?");
				}
				if (index < param.getPropertyTypes().length - 1) {
					hql.append(" AND");
				}
			}
			index++;
		}
		
		String tempHql = hql.toString();
		if (tempHql.endsWith("AND")) {
			tempHql = tempHql.substring(0, tempHql.length() - 3);
			hql = new StringBuffer(tempHql);
		}
		
		if (param.getGroup() != null && param.getGroup().length != 0) {
			hql.append(" GROUP BY ");
			for (int i = 0 ; i < param.getGroup().length ; i++) {
				hql.append("t.");
				hql.append(param.getGroup()[i]);
				if (i < param.getGroup().length - 1) {
					hql.append(",");
				}
			}
		}
		
		order(hql, order);
		
		Query query = session.createQuery(hql.toString());
		
		index = 0;
		int paramIndex = 0;
		for (String propertyValue : param.getPropertyValues()) {
			if (param.getPropertyValues()[index] != null) {
				query.setString(paramIndex, propertyValue);
				paramIndex++;
			}
			index++;
		}
		return query;
	}
	
	/**
	 * 反射条件
	 * @param entity
	 * @param session
	 * @param hql
	 * @return
	 */
	private <T extends AbstractEntity> Query CommonReflexParam(final T entity, Session session, StringBuffer hql, String... order) {
		Map<String, Object> map = new HashMap<String, Object>();
		CommonReflex.getKeyValue(entity, new ArrayList<String>(),  map);
		int index = 0;
		for (Object obj : map.keySet()) {
			if (index == 0) {
				hql.append(" WHERE");
			}
			index++;
			
			hql.append(" t.");
			hql.append(obj);
			hql.append(" = ?");
			
			if (index < map.keySet().size()) {
				hql.append(" AND ");
			}
		}
		
		order(hql, order);
		Query query = session.createQuery(hql.toString());
		
		index = 0;
		for (Object obj : map.keySet()) {
			if (CommonReflex.getPropertyType(entity, String.valueOf(obj)).equals(CommonType.JAVA_LANG_STRING)) {
				query.setString(index, String.valueOf(map.get(obj)));
			} else if (CommonReflex.getPropertyType(entity, String.valueOf(obj)).equals(CommonType.JAVA_LANG_INTEGER)) {
				query.setInteger(index, Integer.parseInt(String.valueOf(map.get(obj))));
			} else if (CommonReflex.getPropertyType(entity, String.valueOf(obj)).equals(CommonType.JAVA_LANG_DOUBLE)) {
				query.setDouble(index, Double.parseDouble(String.valueOf(map.get(obj))));
			} else if (CommonReflex.getPropertyType(entity, String.valueOf(obj)).equals(CommonType.JAVA_LANG_LONG)) {
				query.setLong(index, Long.parseLong(String.valueOf(map.get(obj))));
			} else if (CommonReflex.getPropertyType(entity, String.valueOf(obj)).equals(CommonType.JAVA_UTIL_DATE)) {
				try {
					query.setDate(index, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(String.valueOf(map.get(obj))));
				} catch (ParseException e) {
					throw new DaoException("CommonReflexParam", e);
				}
			}
			index++;
		}
		return query;
	}

	@Override
	public HibernateTemplate hibernateTemplate() {
		return super.getHibernateTemplate();
	}

}
